﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace SZORM
{
    /// <summary>
    /// 提供线程安全的缓存功能，支持基本的添加、获取和原子化的获取或添加操作
    /// </summary>
    public class Cache
    {
        /// <summary>
        /// 线程安全的并发字典，用于存储缓存项
        /// </summary>
        private static readonly ConcurrentDictionary<string, CacheItem> _cache = new();

        /// <summary>
        /// 向缓存中添加项，如果键已存在则不执行任何操作
        /// </summary>
        /// <param name="key">缓存项的唯一标识符</param>
        /// <param name="value">要缓存的对象</param>
        public static void Add(string key, object value)
        {
            Add(key, value, TimeSpan.Zero);
        }

        /// <summary>
        /// 向缓存中添加具有过期时间的项
        /// </summary>
        /// <param name="key">缓存项的唯一标识符</param>
        /// <param name="value">要缓存的对象</param>
        /// <param name="expiration">缓存项的过期时间，TimeSpan.Zero表示永不过期</param>
        public static void Add(string key, object value, TimeSpan expiration)
        {
            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException(nameof(key), "缓存键不能为空");

            var cacheItem = new CacheItem(value, expiration);
            _cache.TryAdd(key, cacheItem);
        }

        /// <summary>
        /// 从缓存中获取项，如果不存在则返回null
        /// </summary>
        /// <param name="key">缓存项的唯一标识符</param>
        /// <returns>缓存的对象或null</returns>
        public static object Get(string key)
        {
            if (string.IsNullOrEmpty(key))
                return null;

            if (_cache.TryGetValue(key, out var cacheItem) && !cacheItem.IsExpired)
                return cacheItem.Value;

            // 移除过期项
            if (cacheItem?.IsExpired == true)
                _cache.TryRemove(key, out _);

            return null;
        }

        /// <summary>
        /// 原子化地获取或添加缓存项
        /// </summary>
        /// <param name="key">缓存项的唯一标识符</param>
        /// <param name="valueFactory">如果键不存在，用于创建值的工厂函数</param>
        /// <returns>现有的或新创建的缓存对象</returns>
        internal static object GetOrAdd(string key, Func<string, object> valueFactory)
        {
            return GetOrAdd(key, valueFactory, TimeSpan.Zero);
        }

        /// <summary>
        /// 原子化地获取或添加具有过期时间的缓存项
        /// </summary>
        /// <param name="key">缓存项的唯一标识符</param>
        /// <param name="valueFactory">如果键不存在，用于创建值的工厂函数</param>
        /// <param name="expiration">缓存项的过期时间</param>
        /// <returns>现有的或新创建的缓存对象</returns>
        internal static object GetOrAdd(string key, Func<string, object> valueFactory, TimeSpan expiration)
        {
            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException(nameof(key), "缓存键不能为空");
            if (valueFactory == null)
                throw new ArgumentNullException(nameof(valueFactory), "值工厂函数不能为空");

            return _cache.GetOrAdd(key, k => new CacheItem(valueFactory(k), expiration)).Value;
        }

        /// <summary>
        /// 表示缓存中的单个项，包含值和过期信息
        /// </summary>
        private class CacheItem
        {
            /// <summary>
            /// 缓存项的值
            /// </summary>
            public object Value { get; }

            /// <summary>
            /// 缓存项的过期时间
            /// </summary>
            public DateTime ExpirationTime { get; }

            /// <summary>
            /// 指示缓存项是否已过期
            /// </summary>
            public bool IsExpired => ExpirationTime != DateTime.MaxValue && DateTime.Now > ExpirationTime;

            /// <summary>
            /// 初始化CacheItem类的新实例
            /// </summary>
            /// <param name="value">缓存项的值</param>
            /// <param name="expiration">过期时间跨度，TimeSpan.Zero表示永不过期</param>
            public CacheItem(object value, TimeSpan expiration)
            {
                Value = value;
                ExpirationTime = expiration == TimeSpan.Zero ? DateTime.MaxValue : DateTime.Now.Add(expiration);
            }
        }
    }
}
